#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>


using namespace std;

const int basesize = 1024;
const int argvnum = 64;
const int envnum = 64;

//全局的命令行参数表
char* gargv[argvnum];
int gargc = 0;

//全局的变量
int lastcode = 0;

//我的系统的环境变量
char* genv[envnum];

//全局的当前shell工作路径
char pwd[basesize];
char pwdenv[basesize + 4];

string GetUserName()
{
    string name = getenv("USER");
    return name.empty() ? "None" : name;
}

string GetPwd()
{
	if(nullptr == getcwd(pwd,sizeof(pwd)))
	{
		return "None";
	}
	snprintf(pwdenv,sizeof(pwdenv),"PWD=%s",pwd);
	putenv(pwdenv);
	return pwd;
}

string LastDir()
{
	string curr = GetPwd();
	if(curr == "/" || curr == "None")
	{
		return curr;
	}
	string lastdir = curr.substr(curr.rfind("/") + 1);
	return lastdir;
}

string MakeCommandLine()
{
	// [ltw@alicloud myshell]$
	char command_line[basesize];
	snprintf(command_line,basesize,"[%s@ %s]# ",\
			GetUserName().c_str(),LastDir().c_str());
	return command_line;
}

void PrintCommandLine()
{
	printf("%s",MakeCommandLine().c_str());
	fflush(stdout);
}


bool GetCommandLine(char command_buffer[],int size)
{
	//我们认为：我们要将用户输入的命令行，当做一个完整的字符串
	//"ls -a -l -n"
	char* result = fgets(command_buffer,size,stdin);
	if(!result)
	{
		return false;
	}
	command_buffer[strlen(command_buffer) - 1] = 0;
	if(strlen(command_buffer) == 0)
	{
		return false;
	}
	return true;
}


void ParseCommandLine(char command_buffer[],int len) //3.分析命令
{
	memset(gargv,0,sizeof(gargv));
	gargc = 0;
	// "ls -a -l -n"
	const char *sep = " ";
	gargv[gargc++] = strtok(command_buffer,sep);

	//这里直接就能写完
	while(gargv[gargc++] = strtok(nullptr,sep));
	gargc--;
}


void debug()
{
	printf("argc: %d\n",gargc);
	for(int i = 0;gargv[i];i++)
	{
		printf("argv[%d]: %s\n",i,gargv[i]);
	}
}


bool ExecuteCommand()   //4.执行命令
{
	//子进程执行命令
	pid_t id = fork();
	if(id < 0)
	{
		return false;
	}
	else if(id == 0)
	{
		//子进程
		//1.执行命令
		execvpe(gargv[0],gargv,genv);
		//2.退出
		exit(1);
	}
	int status = 0;
	pid_t rid = waitpid(id,&status,0);

	if(rid > 0)
	{
		if(WIFEXITED(status))
		{
			lastcode = WEXITSTATUS(status);
		}
		else
		{
			lastcode = 100;
		}
		return true;
	}
	return false;
}


void AddEnv(const char* item)
{
	int index = 0;
	while(genv[index])
	{
		index++;
	}
	genv[index] = (char*)malloc(strlen(item) + 1);
	strncpy(genv[index],item,strlen(item) + 1);
	genv[++index] = nullptr;
}

// shell自己执行命令,本质上是shell调用自己的函数
bool CheckAndExecBuiltCommand()
{
	if(strcmp(gargv[0],"cd") == 0)
	{
		if(gargc == 2)
		{
			chdir(gargv[1]);
			lastcode = 0;
		}
		else
		{
			lastcode = 1;
		}
		return true;
	}
	else if(strcmp(gargv[0],"export") == 0)
	{
		if(gargc == 2)
		{
			AddEnv(gargv[1]);
			lastcode = 0;
		}
		else
		{
			lastcode = 2;
		}
		return true;
	}
	else if(strcmp(gargv[0],"env") == 0)
	{
		for(int i = 0;genv[i]; i++)
		{
			printf("%s\n",genv[i]);
		}
		lastcode = 0;
		return true;
	}
	else if(strcmp(gargv[0],"echo") == 0)
	{
		if(gargc == 2)
		{
			//echo $?
			//echo hello
			if(gargv[1][0] == '$')
			{
				if(gargv[1][1] == '?')
				{
					printf("%d\n",lastcode);
					lastcode = 0;
				}
			}
			else
			{
				printf("%s\n",gargv[1]);
				lastcode = 0;
			}
		}
		else
		{
			lastcode = 3;
		}
		return true;
	}
	return false;
}


//今天我们的shell就直接从父shell中获取环境变量
void InitEnv()
{
	extern char** environ;
	int index = 0;
	while(environ[index])
	{
		genv[index] = (char*)malloc(strlen(environ[index] + 1));
		strncpy(genv[index],environ[index],strlen(environ[index]) + 1);
		index++;
	}
	genv[index] = nullptr;
}


int main()
{
	InitEnv();
	char command_buffer[basesize];
	while(true)
	{
		PrintCommandLine(); //1.命令行提示符

		if(!GetCommandLine(command_buffer,basesize))   //2.获取用户输入
		{
			continue;
		}
		//printf("%s\n",command_buffer);

		ParseCommandLine(command_buffer,strlen(command_buffer)); //3.分析命令
		//debug();

		if(CheckAndExecBuiltCommand())
		{
			continue;
		}

		ExecuteCommand();   //4.执行命令
	}
	return 0;
}
